/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package percussiongenerator.model;

import java.io.File;
import percussiongenerator.sound.IMidiPlayer;
import percussiongenerator.sound.ITimeListener;
import java.util.ArrayList;
import javax.sound.midi.Instrument;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.jfonia.model.PerformanceNode;
import org.jfonia.model.PerformanceSequence;
import org.jfonia.model.TempoNode;
import org.jfonia.model.elements.TimeSignature;
import org.jfonia.model.elements.Tone;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 *
 * @author Jannes Plyson
 */
public class TrackCollection implements ITimeListener{
    private IMidiPlayer midiPlayer;
    private ArrayList<ITrackCollectionListener> listeners;
    private ArrayList<Track> tracks;
    
    public TrackCollection(IMidiPlayer midiPlayer){
        this.midiPlayer = midiPlayer;
        listeners = new ArrayList<ITrackCollectionListener>();
        tracks = new ArrayList<Track>();
    }

    public IMidiPlayer getMidiPlayer(){
        return midiPlayer;
    }

    public void timeUpdated(long time) {        
        for(int i = 0; i < tracks.size(); i++){
            tracks.get(i).timeUpdated(time);
        }
    }

    public void addListener(ITrackCollectionListener listener){
        listeners.add(listener);        
    }

    public void removeListener(ITrackCollectionListener listener){
        listeners.remove(listener);        
    }
  

    public void addTrack(Track track) {
        track.setTrackCollection(this);
        tracks.add(track);        
        try{
            for(ITrackCollectionListener l : listeners){
                l.trackAdded(track);                
            }
        }catch(Exception exc){}
    }

    public void addTracks(ArrayList<Track> tracks) {
        for(Track t : tracks){
            addTrack(t);
        }        
    }

    public void removeTrack(Track track) {
        tracks.remove(track);
        for(ITrackCollectionListener l : listeners){
            l.trackRemoved(track);
        }
    }

    public void removeAllTracks() {
        tracks.clear();
        for(ITrackCollectionListener l : listeners){
            l.cleared();
        }
    }

    public void replaceTracks(ArrayList<Track> newTracks) {
        removeAllTracks();
        addTracks(newTracks);
    }

    public void trackUpdated(Track t){
        for(ITrackCollectionListener l : listeners){
            l.trackUpdated(t);
        }
        if(t.getStaff().getToneSequence().size() <= 0){
            removeTrack(t);
        }                
    }

    public ArrayList<Track> getTracks() {        
        return tracks;
    }

    public int getTrackCount() {
        return tracks.size();
    }

    public void play() {
        midiPlayer.play();
    }

    public void pause() {
        midiPlayer.pause();
    }
    
    public void stop() {
        midiPlayer.stop();
    }

    public boolean isPlayerLoaded(){
        return midiPlayer.isPlayerLoaded();
    }

    public boolean isPlaying(){
        return midiPlayer.isPlaying();
    }

    public Instrument[] getInstruments() {
        return midiPlayer.getInstruments();
    }

    public void addToMidiPlayer(){
        midiPlayer.setTrackCollection(this);
    }

    public long getLongestTrackLength(){
        long max = 0;
        for(int i = 0; i < tracks.size(); i++){
            max = Math.max(max, tracks.get(i).getPerformanceLength());
        }
        return max;
    }

    public long getPlayerTime(){
        return midiPlayer.getTime();
    }

    public long getSoundDelay(){
        return midiPlayer.getSoundDelay();
    }

    public void saveToXML(File file){
        try{
            DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
            Document doc = docBuilder.newDocument();
            Element elTrackCollection = doc.createElement("TrackCollection");
            doc.appendChild(elTrackCollection);
            for(Track track: getTracks()){
                Element elTrack = doc.createElement("Track");
                elTrackCollection.appendChild(elTrack);
                int bpm = ((TempoNode)track.getStaff().getTempoMap().getIntervalFromIndex(0)).getBPM();
                elTrack.setAttribute("bpm", "" + bpm);
                TimeSignature ts = (TimeSignature)track.getStaff().getSequence(TimeSignature.class).getIntervalFromIndex(0);
                elTrack.setAttribute("nominator", "" + ts.getNominator());
                elTrack.setAttribute("denominator", "" + ts.getDenominator());
                elTrack.setAttribute("smallestValue", "" + track.getStaff().getQuantizerNode().getQuantizer().getSmallestValue());
                PerformanceSequence perSeq = track.getStaff().getToneSequence().getPerformanceSequence();
                elTrack.setAttribute("length", "" + perSeq.getDifference());
                elTrack.setAttribute("instrumentName", "" + track.getInstrument().instrument.toString());
                elTrack.setAttribute("drumkitPitch", "" + track.getInstrument().drumkitInstrument);
                Element elTones = doc.createElement("Tones");
                elTrack.appendChild(elTones);
                for(int i = 0; i < perSeq.size(); i++){
                    Element elTone = doc.createElement("Tone");
                    PerformanceNode perNode = (PerformanceNode)perSeq.getIntervalFromIndex(i);
                    elTone.setAttribute("start", "" + perNode.getBegin());
                    elTone.setAttribute("velocity", "" + perNode.getVelocity());
                    elTone.setAttribute("pitch", "" + perNode.getPitch());
                    elTones.appendChild(elTone);
                }               
                Source source = new DOMSource(doc);
                Result result = new StreamResult(file);
                Transformer xformer = TransformerFactory.newInstance().newTransformer();
                xformer.transform(source, result);
            }
        }catch(Exception exc){
            System.err.println(exc.getMessage());
        }
    }

   
}
